using ICSharpCode.SharpZipLib.Checksum;
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using UnityEngine;

namespace PKGames.AssetLoader
{
	public class HTTPDownloader
	{
		public enum Error
		{
			None,
			NoInternet,
			Other
		}

		private const int BufferMaxSize = 262144;

		private const string TemporaryFileExtension = ".download";

		private int _timeout = 15000;

		private int _bufferSize = 262144;

		private object _lock = new object();

		private Thread _thread;

		private string _url;

		private string _pathToSave;

		private bool _streamToFile;

		private byte[] _bytes;

		private volatile AssetRequest.State _downloadStateBeforePause;

		private volatile AssetRequest.State _downloadState;

		private volatile HttpStatusCode _httpStatusCode = HttpStatusCode.OK;

		private volatile float _progress;

		private volatile int _bytesRead;

		private volatile int _totalBytes;

		private long _crc;

		private volatile Error _downloadError;

		private string Tag => "[HTTPDownloader] ";

		public byte[] Bytes => _bytes;

		public AssetRequest.State DownloadState
		{
			get
			{
				lock (_lock)
				{
					return _downloadState;
				}
			}
			set
			{
				lock (_lock)
				{
					_downloadState = value;
				}
			}
		}

		public HttpStatusCode DownloadHttpStatusCode
		{
			get
			{
				lock (_lock)
				{
					return _httpStatusCode;
				}
			}
			set
			{
				lock (_lock)
				{
					_httpStatusCode = value;
				}
			}
		}

		public float Progress
		{
			get
			{
				lock (_lock)
				{
					return _progress;
				}
			}
			set
			{
				lock (_lock)
				{
					_progress = value;
				}
			}
		}

		public int BytesRead
		{
			get
			{
				lock (_lock)
				{
					return _bytesRead;
				}
			}
			set
			{
				lock (_lock)
				{
					_bytesRead = value;
				}
			}
		}

		public int TotalBytes
		{
			get
			{
				lock (_lock)
				{
					return _totalBytes;
				}
			}
			set
			{
				lock (_lock)
				{
					_totalBytes = value;
				}
			}
		}

		public long Crc => _crc;

		public Error DownloadError
		{
			get
			{
				lock (_lock)
				{
					return _downloadError;
				}
			}
			set
			{
				lock (_lock)
				{
					_downloadError = value;
				}
			}
		}

		public HTTPDownloader(string url, string pathToSave, int bufferSize = 262144)
		{
			_url = url;
			_pathToSave = pathToSave;
			_bufferSize = bufferSize;
			_streamToFile = true;
		}

		public HTTPDownloader(string url)
		{
			_url = url;
			_pathToSave = string.Empty;
			_streamToFile = false;
		}

		private void Download()
		{
			_downloadState = AssetRequest.State.Ready;
			if (Application.internetReachability == NetworkReachability.NotReachable)
			{
				_downloadState = AssetRequest.State.Failed;
				_downloadError = Error.NoInternet;
			}
			else
			{
				ServicePointManager.ServerCertificateValidationCallback = (RemoteCertificateValidationCallback)Delegate.Combine(ServicePointManager.ServerCertificateValidationCallback, new RemoteCertificateValidationCallback(AcceptAllCertifications));
				_thread = new Thread(Get);
				_thread.Start();
			}
		}

		public void Pause()
		{
			_downloadStateBeforePause = _downloadState;
			_downloadState = AssetRequest.State.Paused;
			if (_thread != null)
			{
				_thread.Join();
			}
		}

		public void Cancel()
		{
			DownloadState = AssetRequest.State.Failed;
			DownloadError = Error.Other;
		}

		public void Resume()
		{
			if (_downloadState == AssetRequest.State.Paused)
			{
				_downloadState = _downloadStateBeforePause;
				_thread = new Thread(Get);
				_thread.Start();
			}
		}

		public IEnumerator Download(Action<float, int, int> progressionFeedback, float scale)
		{
			Download();
			while (_downloadState == AssetRequest.State.Ready)
			{
				progressionFeedback.Fire(Progress * scale, BytesRead, TotalBytes);
				yield return null;
			}
			progressionFeedback.Fire(Progress * scale, BytesRead, TotalBytes);
		}

		private void Get()
		{
			HttpWebResponse httpWebResponse = null;
			FileStream fileStream = null;
			MemoryStream memoryStream = null;
			Stream stream = null;
			string text = _pathToSave + ".download";
			Crc32 crc = new Crc32();
			try
			{
				HttpWebRequest httpWebRequest = WebRequest.Create(_url) as HttpWebRequest;
				httpWebRequest.Timeout = _timeout;
				if (_streamToFile)
				{
					if (BytesRead == 0)
					{
						fileStream = new FileStream(text, FileMode.Create);
					}
					else
					{
						fileStream = new FileStream(text, FileMode.Append);
						httpWebRequest.AddRange(BytesRead);
					}
					_bytes = new byte[_bufferSize];
				}
				else
				{
					memoryStream = new MemoryStream();
					_bytes = new byte[_bufferSize];
				}
				using (httpWebResponse = (httpWebRequest.GetResponse() as HttpWebResponse))
				{
					UnityEngine.Debug.Log(Tag + "Status Code: " + httpWebResponse.StatusCode);
					if (httpWebResponse.StatusCode == HttpStatusCode.OK)
					{
						_progress = 0f;
						_bytesRead = 0;
						TotalBytes = (int)httpWebResponse.ContentLength;
					}
					if (httpWebResponse.StatusCode == HttpStatusCode.OK || httpWebResponse.StatusCode == HttpStatusCode.PartialContent)
					{
						stream = httpWebResponse.GetResponseStream();
						stream.ReadTimeout = _timeout;
						int num = 0;
						while ((num = stream.Read(_bytes, 0, _bytes.Length)) > 0 && DownloadState != AssetRequest.State.Paused && DownloadState != AssetRequest.State.Failed)
						{
							crc.Update(_bytes, 0, num);
							BytesRead += num;
							if (_streamToFile)
							{
								fileStream.Write(_bytes, 0, num);
							}
							else
							{
								memoryStream.Write(_bytes, 0, num);
							}
							Progress = (float)BytesRead / (float)TotalBytes;
						}
						if (DownloadState != AssetRequest.State.Paused && BytesRead < TotalBytes)
						{
							throw new DownloadInterruptedException("Could Not finish downloading file: " + _pathToSave);
						}
						if (_streamToFile)
						{
							fileStream.Flush();
							fileStream.Close();
						}
						else
						{
							memoryStream.Flush();
							memoryStream.Close();
							_bytes = memoryStream.ToArray();
						}
						stream.Close();
						if (DownloadState != AssetRequest.State.Paused)
						{
							_crc = crc.Value;
							if (_streamToFile)
							{
								FileHelper.RenameFile(text, _pathToSave);
							}
							Progress = 1f;
							DownloadState = AssetRequest.State.Successful;
						}
					}
					DownloadHttpStatusCode = httpWebResponse.StatusCode;
				}
			}
			catch (Exception ex)
			{
				UnityEngine.Debug.LogWarning(Tag + "HTTP GET ERROR: " + ex.ToString() + "\nUrl: " + _url + "\nPath: " + _pathToSave);
				WebException ex2 = ex as WebException;
				if (ex2 != null)
				{
					HttpWebResponse httpWebResponse3 = ex2.Response as HttpWebResponse;
					if (httpWebResponse3 != null)
					{
						DownloadHttpStatusCode = httpWebResponse3.StatusCode;
					}
				}
				fileStream?.Close();
				memoryStream?.Close();
				stream?.Close();
				httpWebResponse?.Close();
				if (_streamToFile)
				{
					FileHelper.DeleteFile(text);
				}
				DownloadState = AssetRequest.State.Failed;
				DownloadError = Error.Other;
			}
			if (_streamToFile)
			{
				_bytes = null;
			}
		}

		public bool AcceptAllCertifications(object sender, X509Certificate certification, X509Chain chain, SslPolicyErrors sslPolicyErrors)
		{
			return true;
		}
	}
}
